/*
 * Copyright © 2004-2014 chenYuan. All rights reserved.
 * @Website:wwww.jspx.net
 * @Mail:39793751@qq.com
 * @author: chenYuan , 陈原
 * @License: Jspx.net Framework Code is open source (LGPL)，Jspx.net Framework 使用LGPL 开源授权协议发布。
 * @jvm:jdk1.6+  x86/amd64
 *
 */
package com.jspx.sioc.config;

import com.jspx.boot.EnvFactory;
import com.jspx.boot.environment.Environment;
import com.jspx.boot.environment.EnvironmentTemplate;
import com.jspx.io.AbstractRead;
import com.jspx.io.AutoReadTextFile;
import com.jspx.io.ReadHtml;
import com.jspx.sioc.tag.IncludeElement;
import com.jspx.sioc.IocContext;
import com.jspx.sioc.Sioc;
import com.jspx.sioc.tag.BeanElement;
import com.jspx.sioc.tag.SiocElement;
import com.jspx.utils.*;
import com.jspx.scriptmark.XmlEngine;
import com.jspx.scriptmark.parse.XmlEngineImpl;
import com.jspx.scriptmark.core.TagNode;
import java.io.IOException;
import java.io.File;
import java.net.URL;
import java.util.*;
import java.util.logging.Logger;

/**
 * Created by IntelliJ IDEA.
 * User:chenYuan (mail:39793751@qq.com)
 * Date: 2007-2-12
 * Time: 12:27:19
 * 1.0
 */
public class ConfigureContext implements IocContext {
    final static private Logger log = java.util.logging.Logger.getAnonymousLogger();
    private static IocContext instance = new ConfigureContext();
    //永远不清除，并且保留
    private static Map<String, Map<String, BeanElement>> registerBeanMap = new HashMap<String, Map<String, BeanElement>>();
    private static Map<String, Map<String, BeanElement>> beanElementMap = new HashMap<String, Map<String, BeanElement>>() ;
    private static List<BeanElement> elementList = new ArrayList<BeanElement>();
    //继承关系表
    private static Map<String, String> extendMap = new HashMap<String, String>();
    private static Map<String, String> applicationMap = new HashMap<String, String>();

    private static EnvironmentTemplate envTemplate = EnvFactory.getEnvironmentTemplate();
    private String[] configFile;
    private static String[] readFile;

    public static IocContext getInstance() {
        return instance;
    }


    private ConfigureContext() {


    }


    /**
     * Map<String,Map<String,BeanElement>>  Map<命名空间,Map<bean name,BeanElement>>
     * 得到bean 命名空间
     * @throws Exception 异常
     */

   public void reload() throws Exception {

       synchronized (ConfigureContext.class)
       {
           for (String namespace : beanElementMap.keySet()) {
               Map<String, BeanElement> beanMap = beanElementMap.get(namespace);
               beanMap.clear();
           }
           beanElementMap.clear();
           extendMap.clear();
           elementList.clear();
           applicationMap.clear();

           List<TagNode> iocElements = getSiocElements();
           for (TagNode element : iocElements) {
               SiocElement siocElement = (SiocElement) element;
               String namespace = StringUtil.fixedNamespace(siocElement.getNamespace());
               String extend = siocElement.getExtends();
               String appName =  siocElement.getApplication();
               if (!StringUtil.isNULL(appName)) applicationMap.put(namespace,appName);
               extendMap.put(namespace, extend);
               Map<String, BeanElement> beanElements = beanElementMap.get(namespace);
               if (beanElements == null) {
                   beanElements = new HashMap<String, BeanElement>();
                   beanElementMap.put(namespace, beanElements);
               }
               List<TagNode> beanLists = siocElement.getBeanElements();
               for (TagNode aElement : beanLists) {
                   BeanElement beanElement = (BeanElement) aElement;
                   String[] aliases = beanElement.getAliases();
                   if (aliases != null) {
                       for (String name : aliases) {
                           beanElements.put(name, beanElement);
                       }
                   }
                   beanElements.put(beanElement.getId(), beanElement);
               }
               beanLists.clear();
           }
           iocElements.clear();

           if (!registerBeanMap.isEmpty()) {
               for (String namespace : registerBeanMap.keySet()) {
                   Map<String, BeanElement> regBeanMap = beanElementMap.get(namespace);
                   if (regBeanMap != null && !regBeanMap.isEmpty())
                       beanElementMap.put(namespace, registerBeanMap.get(namespace));
               }
           }

           //生成总列表
           for (String namespace : beanElementMap.keySet()) {
               Map<String, BeanElement> map = beanElementMap.get(namespace);
               if (map != null)
                   for (BeanElement beanElement : map.values()) {
                       beanElement.setNamespace(namespace);
                       elementList.add(beanElement);
                   }
           }
       }

    }

    public void setConfigFile(String file) {
        String[] configFile = new String[]{file};
        setConfigFile(configFile);
    }

    public void setConfigFile(String[] configFile) {
        this.configFile = configFile;

        try {
            reload();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void registerBeanElement(Map<String, BeanElement> beanElements, String namespace) {
        Map<String, BeanElement> temp = registerBeanMap.get(namespace);
        if (temp == null) {
            temp = new HashMap<String, BeanElement>();
            registerBeanMap.put(namespace, temp);
        }
        temp.putAll(beanElements);
    }

    private String readFile(String fileName) throws IOException {

        String fileNamePath = fileName;
        if (!FileUtil.isFileExist(fileName)) {
            URL url = ClassUtil.getResource(fileName);
            if (url != null) {
                fileNamePath = url.getPath();
            }
            if (!FileUtil.isFileExist(fileNamePath)) {
                fileNamePath = envTemplate.getString(Environment.defaultPath) + fileName;
            }
            if (!FileUtil.isFileExist(fileNamePath)) {
                throw new IOException("sioc no find fileName Path: " + fileNamePath + "<br/>\r\n不能找到配置文件:" + fileNamePath);
            }
        }

        AbstractRead abstractRead = new AutoReadTextFile();
        abstractRead.setEncode(envTemplate.getString(Environment.encode, Environment.defaultEncode));
        abstractRead.setFile(fileNamePath);
        return abstractRead.getContent();
    }

    private String readHttpFile(String fileName) throws IOException {
        if (StringUtil.isNULL(fileName)) return StringUtil.empty;
        AbstractRead abstractRead = new ReadHtml();
        if (fileName.toLowerCase().contains("gb"))
            abstractRead.setEncode("GBK");
        else if (fileName.toLowerCase().contains("utf-8"))
            abstractRead.setEncode(Environment.defaultEncode);
        else envTemplate.getString(Environment.encode, Environment.defaultEncode);
        abstractRead.setFile(fileName);
        return abstractRead.getContent();
    }


    /**
     * 读取文件,支持url 方式
     *
     * @param fileName 读取文件
     * @return 文件内容
     * @throws Exception 运行错误
     */
    private String readContext(String fileName) throws Exception {
        String configString;
        if (fileName.startsWith("http")) {
            configString = readHttpFile(fileName);
        } else {
            configString = readFile(fileName);
        }
        return EnvFactory.getPlaceholder().processTemplate(envTemplate.getVariableMap(), configString);
    }

    /**
     * @param fileName 文件
     * @return 读取所有配置文件
     * @throws Exception 异常
     */
    private List<TagNode> getIocElementsForFile(String fileName) throws Exception {
            fileName = FileUtil.mendFile(fileName);
            if (ArrayUtil.inArray(readFile, FileUtil.getFileName(fileName), true) || StringUtil.isNULL(fileName)) {
                return new ArrayList<TagNode>();
            }

            readFile  = ArrayUtil.add(readFile, FileUtil.getFileName(fileName));

            if (fileName.contains("*") || fileName.contains("?") || fileName.contains("#") || fileName.contains("$")) {
                List<TagNode> results = new ArrayList<TagNode>();
                File[] findFiles = FileUtil.getPatternFiles(envTemplate.getString(Environment.defaultPath), fileName);
                for (File f : findFiles) {
                    results.addAll(getIocElementsForFile(f.getAbsolutePath()));
                }
                return results;
            }

        XmlEngine xmlEngine = new XmlEngineImpl();
        xmlEngine.putTag(SiocElement.tagName, SiocElement.class.getName());
        String txt = readContext(fileName);
        log.info("sioc load file:" + fileName);
        List<TagNode> results = xmlEngine.getTagNodes(txt);
        String[] includeFiles = null;
        for (TagNode tNode : results) {
            SiocElement se = (SiocElement) tNode;
            for (TagNode iNode : se.getIncludeElements()) {
                IncludeElement node = (IncludeElement) iNode;
                includeFiles = ArrayUtil.add(includeFiles, node.getFile());
            }
        }
        if (includeFiles != null)
            for (String f : includeFiles) {
                if (f == null) continue;
                results.addAll(getIocElementsForFile(f));
            }
        return results;
    }

    /**
     * @return 得到基本的配置<sioc></sioc>
     * @throws Exception 异常
     */
    private List<TagNode> getSiocElements() throws Exception {
        readFile = null;
        List<TagNode> results = new ArrayList<TagNode>();
        for (String fileName : configFile) {
            results.addAll(getIocElementsForFile(fileName));
        }
        return results;
    }


    public List<BeanElement> getElementList() throws Exception {
        return elementList;
    }

    /**
     * @param beanName  得到 bean名称
     * @param namespace 命名空间
     * @param reload    是否从新载入
     * @return 返回配置
     * @throws Exception 异常
     */
    private BeanElement getBeanElement(String beanName, String namespace, boolean reload) throws Exception {
        String nameK = namespace;
        if (StringUtil.isNULL(nameK)) nameK = Sioc.global;
        if (beanName == null) return null;
        Map<String, BeanElement> elementMap;
        BeanElement result = null;
        while (!StringUtil.isNULL(nameK) && !Sioc.global.equalsIgnoreCase(nameK)) {

            elementMap = beanElementMap.get(nameK);
            if (elementMap != null && !elementMap.isEmpty())
            {
                result = elementMap.get(beanName);
                if (result != null)
                {
                    return result;
                }
            }
            if (extendMap.containsKey(nameK)) {
                nameK = extendMap.get(nameK);
            }
            else {
                if (nameK.contains("/")) nameK = StringUtil.substringBeforeLast(nameK, "/");
                else nameK = Sioc.global;
            }
        }
        elementMap = beanElementMap.get(Sioc.global);
        if (elementMap != null) result = elementMap.get(beanName);

        if (reload && result == null) {
            reload();
            return getBeanElement(beanName, namespace, false);
        }
        return result;
    }

    /**
     * @param beanName name
     * @param namespace 命名空间
     * @return 直接返回本命名空间配置数据
     * @throws Exception 异常
     */
    public BeanElement getBeanElementConfig(String beanName, String namespace) throws Exception {
        String nameK = namespace;
        if (StringUtil.isNULL(nameK)) nameK = Sioc.global;
        final Map<String, BeanElement> elementMap = beanElementMap.get(nameK);
        if (elementMap == null || elementMap.isEmpty()) return null;
        return elementMap.get(beanName);
    }

    /**
     * 得到bean 配置
     *
     * @param beanName  名称
     * @param namespace 命名空间
     * @return bean配置
     * @throws Exception 异常
     *                   解析错误
     */
    public BeanElement getBeanElement(String beanName, String namespace) throws Exception {
        return getBeanElement(beanName, namespace, true);
    }

    /**
     * @param beanName  名称
     * @param namespace 命名空间
     * @return 判断是否存在
     * @throws Exception 异常
     *                   解析错误
     */
    public boolean containsBean(String beanName, String namespace) throws Exception {
        return getBeanElement(beanName, namespace, false) != null;
    }

    /**
     * @return 继承关心
     * @throws Exception 异常
     */
    public Map<String, String> getExtendMap() throws Exception {
        return extendMap;
    }

    /**
     * 得到应用名称列表
     * @return map 方式
     */
    public Map<String, String> getApplicationMap()
    {
        return applicationMap;
    }


}